package com.bfxy.disruptor.common.balance;

import com.bfxy.disruptor.common.Util;

import sun.misc.Unsafe;

//L是左边，R是右边，Value是中间。操作系统缓存行是64字节，Long类型占8个字节。
//Value左右都是7个保证Value独占一个缓存行。
class LhsPadding {
	protected long p1, p2, p3, p4, p5, p6, p7;
}

class Value extends LhsPadding {
	protected volatile long value;
}

class RhsPadding extends Value {
	protected long p9, p10, p11, p12, p13, p14, p15;
}

//每一个消费者都会有一个Sequence，所有的生产者公用一个Sequence，
//将生产者共用的Sequence和每个消费者的Sequence做对比，消费者调用waitfor进行等待，
//每一个槽都有Sequence序号，多个生产者共用一个Sequence就可以了，这个Sequence不是生产者的，而是RingBuffer的，
//消费者不一样，消费者可以一次拿多个数据，
//生产者有自己的序号，RingBuffer有自己的序号，消费者也有自己的序号
//每一个消费者都有自己独立的SequenceBarrier和Sequence,Sequence作用是每一个消费者消费到哪了，每一个消费者的进度，这个进度要让ringBuffer知道，
/*
 消费者1成功消费了0,没有成功消费1，消费者2成功消费了2,3没有成功消费5，消费者3成功消费了4。此时0,2,3,4成功消费了，
 但是1没有成功消费，生产者会选择3个消费者成功消费的最小序号0，往0投放第10个数据，2,3,4空着了，生产者只能投放0，
 不能跳过1去投放2,3,4。此时生产者就要阻塞等待。
这就是消费者的平衡，每一个消费者都要有一个序号，存储消费进度。消费者的进度要存储在ringbuffer中。
 */
public class Sequence extends RhsPadding {//通过继承表示64位。Sequence就是对value的封装，就是数组中的游标位置。
	//采用缓存行填充的方式对long类型的一层包装，用以代表事件的序号。通过unsafe的cas方法从而避免了锁的开销；
	//由于需要在线程间共享，所以Sequence是引用传递，并且是线程安全的；再次，Sequence支持CAS操作；最后，为了提高效率，Sequence通过padding来避免伪共享。
	public static final long INITIAL_VALUE = -1L;
	private static final Unsafe UNSAFE;
	public static final long VALUE_OFFSET;

	static {
		UNSAFE = Util.getUnsafe();// sun.misc.Unsafe@32d992b2
		try {
			VALUE_OFFSET = UNSAFE.objectFieldOffset(Value.class.getDeclaredField("value"));// 72
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}

	public Sequence() {
		this(INITIAL_VALUE);
	}

	public Sequence(final long initialValue) {
		UNSAFE.putOrderedLong(this, VALUE_OFFSET, initialValue);
	}

	public long get() {
		return value;
	}

	public void set(final long value) {
		UNSAFE.putOrderedLong(this, VALUE_OFFSET, value);
	}

	public void setVolatile(final long value) {
		UNSAFE.putLongVolatile(this, VALUE_OFFSET, value);
	}

	public boolean compareAndSet(final long expectedValue, final long newValue) {
		return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, expectedValue, newValue);
	}

	public long incrementAndGet() {
		return addAndGet(1L);
	}

	public long addAndGet(final long increment) {
		long currentValue;
		long newValue;

		do {
			currentValue = get();
			newValue = currentValue + increment;
		} while (!compareAndSet(currentValue, newValue));

		return newValue;
	}

	@Override
	public String toString() {
		return Long.toString(get());
	}
}
